#include "WidgetResizer.h"

#include <QEvent>
#include <QMouseEvent>
#include <QWidget>

#define s_drag_detect_dis 2

WidgetResizer::WidgetResizer(QWidget* w) : QObject(w) {
  Q_ASSERT(w != nullptr);
  m_hostWidget = w;
  if (m_hostWidget->sizePolicy() !=
      QSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed)) {
    m_hostWidget->setMouseTracking(true);
    m_hostWidget->installEventFilter(this);
  }
}

void WidgetResizer::enable(bool v) {
  if (v) {
    m_hostWidget->setMouseTracking(true);
    m_hostWidget->installEventFilter(this);
  } else
    m_hostWidget->removeEventFilter(this);
}

bool WidgetResizer::eventFilter(QObject* watched, QEvent* event) {
  if (event->type() == QEvent::MouseMove) {
    QMouseEvent* e = static_cast<QMouseEvent*>(event);
    QPoint mousePos = e->pos();
    if (m_dragging) {
      mousePos = e->globalPos();
      switch (m_dragArea) {
        case kNone:
          break;
        case kLeft: {
          int delta = mousePos.x() - m_dragPos.x();
          m_hostWidget->setGeometry(
              m_hostWidget->geometry().adjusted(delta, 0, 0, 0));
        } break;
        case kTop: {
          int delta = mousePos.y() - m_dragPos.y();
          m_hostWidget->setGeometry(
              m_hostWidget->geometry().adjusted(0, delta, 0, 0));
        } break;
        case kRight: {
          int delta = mousePos.x() - m_dragPos.x();
          m_hostWidget->resize(
              m_hostWidget->size().grownBy(QMargins(0, 0, delta, 0)));
        } break;
        case kBottom: {
          int delta = mousePos.y() - m_dragPos.y();
          m_hostWidget->setGeometry(
              m_hostWidget->geometry().adjusted(0, 0, 0, delta));
        } break;
        case kTopLeft: {
          int deltaX = mousePos.x() - m_dragPos.x();
          int deltaY = mousePos.y() - m_dragPos.y();
          m_hostWidget->setGeometry(
              m_hostWidget->geometry().adjusted(deltaX, deltaY, 0, 0));
        } break;
        case kTopRight: {
          int deltaX = mousePos.x() - m_dragPos.x();
          int deltaY = mousePos.y() - m_dragPos.y();
          m_hostWidget->setGeometry(
              m_hostWidget->geometry().adjusted(0, deltaY, deltaX, 0));
        } break;
        case kBottomRight: {
          int deltaX = mousePos.x() - m_dragPos.x();
          int deltaY = mousePos.y() - m_dragPos.y();
          m_hostWidget->setGeometry(
              m_hostWidget->geometry().adjusted(0, 0, deltaX, deltaY));
        } break;
        case kBottomLeft: {
          int deltaX = mousePos.x() - m_dragPos.x();
          int deltaY = mousePos.y() - m_dragPos.y();
          m_hostWidget->setGeometry(
              m_hostWidget->geometry().adjusted(deltaX, 0, 0, deltaY));
        } break;
        default:
          break;
      }

      m_dragPos = mousePos;
    } else if (m_hostWidget->windowState() != Qt::WindowMaximized &&
               m_hostWidget->windowState() != Qt::WindowFullScreen) {
      DragArea area = kNone;
      QRect r = m_hostWidget->rect();
      if ((r.topLeft() - mousePos).manhattanLength() <= 4)
        area = kTopLeft;
      else if ((r.topRight() - mousePos).manhattanLength() <= 4)
        area = kTopRight;
      else if ((r.bottomRight() - mousePos).manhattanLength() <= 4)
        area = kBottomRight;
      else if ((r.bottomLeft() - mousePos).manhattanLength() <= 4)
        area = kBottomLeft;
      else if (mousePos.x() - r.left() <= s_drag_detect_dis)
        area = kLeft;
      else if (r.right() - mousePos.x() <= s_drag_detect_dis)
        area = kRight;
      else if (mousePos.y() - r.top() <= s_drag_detect_dis)
        area = kTop;
      else if (r.bottom() - mousePos.y() <= s_drag_detect_dis)
        area = kBottom;

      m_dragArea = area;
      if (m_dragArea != kNone) {
        if (m_dragArea == kLeft || m_dragArea == kRight)
          m_hostWidget->setCursor(Qt::CursorShape::SizeHorCursor);
        else if (m_dragArea == kTop || m_dragArea == kBottom)
          m_hostWidget->setCursor(Qt::CursorShape::SizeVerCursor);
        else if (m_dragArea == kTopLeft || m_dragArea == kBottomRight)
          m_hostWidget->setCursor(Qt::CursorShape::SizeFDiagCursor);
        else
          m_hostWidget->setCursor(Qt::CursorShape::SizeBDiagCursor);
      } else
        m_hostWidget->setCursor(Qt::CursorShape::ArrowCursor);
    }
  } else if (event->type() == QEvent::MouseButtonPress) {
    QMouseEvent* e = static_cast<QMouseEvent*>(event);
    if (m_dragArea != kNone && e->button() == Qt::LeftButton) {
      m_dragging = true;
      m_dragPos = e->globalPos();
    }
  } else if (event->type() == QEvent::MouseButtonRelease) {
    QMouseEvent* e = static_cast<QMouseEvent*>(event);
    if (e->button() == Qt::LeftButton) {
      m_hostWidget->setCursor(Qt::CursorShape::ArrowCursor);
      resetDragState();
    }
  } else if (event->type() == QEvent::Leave) {
    m_hostWidget->setCursor(Qt::CursorShape::ArrowCursor);
    resetDragState();
  }

  return QObject::eventFilter(watched, event);
}

void WidgetResizer::resetDragState() {
  m_dragging = false;
  m_dragArea = kNone;
}
